package org.codefilarete.tool.bean;

import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.codefilarete.tool.collection.ArrayIterator;
import org.codefilarete.tool.collection.ReadOnlyIterator;

/**
 * An {@link java.util.Iterator} that gets its information from each class of a hierarchy.
 * 
 * @author Guillaume Mary
 */
public abstract class InheritedElementIterator<T> extends ReadOnlyIterator<T> {
	
	protected Iterator<Class<?>> classIterator;
	protected Iterator<T> inheritedElementIterator = Collections.emptyIterator();
	
	public InheritedElementIterator(Class<?> aClass) {
		this(new ClassIterator(aClass));
	}
	
	public InheritedElementIterator(Iterator<Class<?>> classIterator) {
		this.classIterator = classIterator;
	}
	
	@Override
	public boolean hasNext() {
		// simple case
		if (inheritedElementIterator.hasNext()) {
			return true;
		} else {
			// no more element for the current iterator => we must scan upper classes if they have some
			while (classIterator.hasNext() && !inheritedElementIterator.hasNext()) {
				// transforming the class elements to an Iterator
				inheritedElementIterator = nextInheritedElementIterator(classIterator.next());
			}
			return inheritedElementIterator.hasNext();
		}
	}
	
	protected Iterator<T> nextInheritedElementIterator(Class clazz) {
		return new ArrayIterator<>(getElements(clazz));
	}
	
	@Override
	public T next() {
		if (!hasNext()) {
			throw new NoSuchElementException();
		}
		return inheritedElementIterator.next();
	}
	
	/**
	 * Gives any element from a class: field, method, interface, ...
	 *
	 * @param clazz the class for which elements must be given
	 * @return an array of elements, not null
	 */
	protected abstract T[] getElements(Class<?> clazz);
}
